锁,一般和事务搭配着使用

1. 行级锁

  • 基于数据行

  • 通俗理解: 每一次只允许一个用户对该条数据进行修改,在orm中只有当事务执行完,锁才会被释放(在SQL中只能手动释放锁),下一个用户才能修改该条数据

  • 行级锁,一般都会在查询数据的时候上锁

# .select_for_update() 上行级锁

book_list = Book.objects.filter(price__gt=50).select_for_update()  # 对查询到的数据上锁,每一次只允许一个用户对该条数据进行修改
book_list.update(price=100)  # 批量更新数据

2. 表锁 -> 了解即可

  • 基于表

  • 通俗理解: 每一次只允许一个用户对该表进行操作,在orm中只有当事务执行完,锁才会被释放(在SQL中只能手动释放锁),下一个用户才能操作该表

class LockingManager(models.Manager):

    """ Add lock/unlock functionality to manager.

    Example::

        class Job(models.Model):

            manager = LockingManager()

            counter = models.IntegerField(null=True, default=0)

            @staticmethod
            def do_atomic_update(job_id)
                ''' Updates job integer, keeping it below 5 '''
                try:
                    # Ensure only one HTTP request can do this update at once.
                    Job.objects.lock()

                    job = Job.object.get(id=job_id)
                    # If we don't lock the tables two simultanous
                    # requests might both increase the counter
                    # going over 5
                    if job.counter < 5:
                        job.counter += 1                                        
                        job.save()

                finally:
                    Job.objects.unlock()

    """    

    def lock(self):
""" Lock table.

        Locks the object model table so that atomic update is possible.
        Simulatenous database access request pend until the lock is unlock()'ed.

        Note: If you need to lock multiple tables, you need to do lock them
        all in one SQL clause and this function is not enough. To avoid
        dead lock, all tables must be locked in the same order.

        See http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
        """
        cursor = connection.cursor()
        table = self.model._meta.db_table
        logger.debug("Locking table %s" % table)
        cursor.execute("LOCK TABLES %s WRITE" % table)
        row = cursor.fetchone()
        return row

    def unlock(self):
""" Unlock the table. """
        cursor = connection.cursor()
        table = self.model._meta.db_table
        cursor.execute("UNLOCK TABLES")
        row = cursor.fetchone()
        return row  

事务


  • 这里的事务和MySQL中的一样,当发生了错误就回滚到最初状态,不懂的可以看回Mysql中的事务(表操作 -> innodb引擎)

  • 导入事务模块

from django.db import transaction

  • 事务的使用

# 固定写法

import datetime
from app01 import models
from django.db import transaction
try:
    with transaction.atomic():
# 查询语句
except Exception as e:
    print(str(e))

  • with transaction.atomic() 里面所写的代码发生报错,所有的东西都会回滚到最初的状态,例如: '火星出版社' 不会被添加到数据库中

import datetime
from app01 import models
from django.db import transaction
try:
    with transaction.atomic():
        new_publisher = models.Publisher.objects.create(name="火星出版社")
        models.Book.objects.create(title="橘子物语", publish_date=datetime.date.today(), publisher_id=10)  # 指定一个不存在的出版社id
except Exception as e:
    print(str(e))

事务和锁搭配使用


  • 只有当事务执行完后,锁才会被释放

import time
from app01.models import *
from django.db import transaction

try:
    with transaction.atomic():
        time.sleep(5)  # 延时5秒执行下方代码
        book_list = Book.objects.filter(price__gt=50).select_for_update()  # 对查询到的数据上锁,每一次只允许一个用户对该条数据进行修改
        book_list.update(price=100)  # 批量更新数据
except Exception as e:
    print(str(e))